package com.easy.tool.manual;

import com.easy.tool.anno.BeanDes;
import com.easy.tool.anno.MethodDes;
import com.easy.tool.entity.ManualEntity;
import com.easy.tool.entity.ManualMethodEntity;
import lombok.Value;
import org.apache.log4j.Logger;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.lang.annotation.IncompleteAnnotationException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.URL;
import java.net.URLDecoder;
import java.util.*;

/**
 * 获取手册信息
 * @author 高俊
 * @create 2022-04-2022/4/8-16:10
 */
public class GetManual extends FileTreeAndCount {
    private Logger logger = Logger.getLogger(FileTreeAndCount.class);

    private String dirPathFlag = "target/classes";
    private List<ManualEntity> result = new ArrayList<>(20);


    /**
     * 主方法
     * @return
     */
    public List<ManualEntity>  manualMain(){
        String packageName = "com.easy.tool.utils";
        // 获取文件树  （调用）
        Map<String, List<String>> map = GetManual(packageName);
        for (Map.Entry<String, List<String>> n : map.entrySet()) {
            String key = n.getKey();
            List<String> list = map.get(key);
//            logger.debug("==============================>当前工具类型：" + key);
            loopList(key,list);

        }
        return result;
    }

    /**
     * 获取方法注解、参数类型、变量
     * @param list
     * @return
     */
    private void loopList(String key, List<String> list){
        for (int i = 0; i < list.size(); i++) {
            ManualEntity manualEntity = new ManualEntity();
            List<ManualMethodEntity> methodList = new ArrayList<>();
            try {
                String packageName = list.get(i);
                if(packageName.indexOf("$") < 0){
                    manualEntity.setBeanName(packageName.substring(packageName.lastIndexOf(".")+1));
                    Class<?> clazz = Class.forName(packageName);
                    // 获取bean的注解
                    BeanDes anno = clazz.getAnnotation(BeanDes.class);
                    if (anno != null){
                        // 获取并存入entity中
                        manualEntity.setAnnoName(anno.name());
                        manualEntity.setAnnoDes(anno.des());
                    }


                    Method[] methods = clazz.getMethods();
                    // 循环方法列表 (调用)
                    loopMethod(methods,methodList);

                    manualEntity.setMethodList(methodList);
                    manualEntity.setBeanType(key);
                    result.add(manualEntity);
                }
            }catch (Exception e){
                logger.error(e.getMessage());
                e.printStackTrace();
            }

        }
    }



    /*-----------------------------------------------------------------------------------------------------------------*/

    /**
     * 循环 遍历 方法
     * @param methods
     * @param methodList
     * @return
     */
    private List<ManualMethodEntity> loopMethod(Method[] methods,List<ManualMethodEntity> methodList){
        for (Method m : methods) {
            ManualMethodEntity manualMethodEntity = new ManualMethodEntity();
            manualMethodEntity.setReturnType(m.getReturnType().getSimpleName());
            StringBuilder builder = new StringBuilder();
            // 获取方法注解
            MethodDes anno = m.getAnnotation(MethodDes.class);

            if (anno != null){
                // 设置注解的des描述
                manualMethodEntity.setMethodDes(anno.des());
                manualMethodEntity.setExplain(anno.explain());
                manualMethodEntity.setParamDes(anno.param());

                // ----------------- 拼接方法名称和参数   开始-----------------
                builder.append(manualMethodEntity.getReturnType());
                builder.append(" ");
                builder.append(m.getName());
                builder.append("(");
                // 获取参数列表
                Parameter[] parameters = m.getParameters();
                // 循环参数数组
                for (int j = 0; j < parameters.length; j++) {
                    Parameter pa = parameters[j];
                    builder.append(pa.getType().getSimpleName() + " " + pa.getName());
                    if (j != parameters.length-1){
                        builder.append(", ");
                    }
                }
                builder.append(")");
                // ----------------- 拼接方法名称和参数   结束-----------------
                manualMethodEntity.setMethodName(builder);
//            System.out.println(builder);
                methodList.add(manualMethodEntity);
            }

        }
        return methodList;
    }

    /**
     * 获取指定包下的绝对路径，返回文件树
     */
    private Map<String, List<String>> GetManual(String packAgeName) {
        Map<String, List<String>> map = new HashMap<>();
        // 获取包的名字 并进行替换
        String packageDirName = packAgeName.replace('.', '/');
        // 定义一个枚举的集合 并进行循环来处理这个目录下的things
        Enumeration<URL> dirs;

        try {
            dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
            while (dirs.hasMoreElements()){
                // 获取下一个元素
                URL url = dirs.nextElement();
                // 得到协议的名称
                String protocol = url.getProtocol();

                // 获取当前 本地绝对 路径
                String filePath = URLDecoder.decode(url.getFile(), "UTF-8");

                // 更正当前路径   因为filePath第一个字符会是 /
                filePath = filePath.substring(1);

                if (filePath.indexOf(dirPathFlag)>=0){
//                    logger.debug("当前遍历目录：" + filePath);
                    map = ManualFileTree(filePath);
                }
            }
        }catch (Exception e){
            logger.error(e.getMessage());
            e.printStackTrace();
        }
        return map;
    }

    public static String readStream(InputStream in) {
        try {
            //<1>创建字节数组输出流，用来输出读取到的内容
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            //<2>创建缓存大小
            byte[] buffer = new byte[1024]; // 1KB
            //每次读取到内容的长度
            int len = -1;
            //<3>开始读取输入流中的内容
            while ((len = in.read(buffer)) != -1) { //当等于-1说明没有数据可以读取了
                baos.write(buffer, 0, len);   //把读取到的内容写到输出流中
            }
            //<4> 把字节数组转换为字符串
            String content = baos.toString();
            //<5>关闭输入流和输出流
            in.close();
            baos.close();
            //<6>返回字符串结果
            return content;
        } catch (Exception e) {
            e.printStackTrace();
            return  e.getMessage();
        }
    }
}
